package org.iabako.client.ui.page.home;

import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.visualization.client.AbstractDataTable;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.LegendPosition;
import com.google.gwt.visualization.client.VisualizationUtils;
import com.google.gwt.visualization.client.visualizations.corechart.*;
import org.iabako.client.tools.IabakoAsyncCallback;
import org.iabako.client.tools.Messages;
import org.iabako.client.ui.layout.field.LabelText;
import org.iabako.client.user.AuthenticateUserProvider;
import org.iabako.shared.dto.FinancesChartData;
import org.iabako.shared.entity.enumeration.Month;
import org.iabako.shared.iservice.business.FinanceServiceAsync;
import org.iabako.shared.tools.GenericTools;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * TODO javadoc
 * Date: 24/11/13
 * Time: 17:53
 */
public class HomePage extends Composite {

    interface MyUiBinder extends UiBinder<Panel, HomePage> {
    }

    private static final MyUiBinder binder = GWT.create(MyUiBinder.class);

    protected final FinanceServiceAsync financeServiceAsync = FinanceServiceAsync.Util.getInstance();

    @UiField
    HTMLPanel financialCharts;
    @UiField
    HTMLPanel profitChartPanel;
    @UiField
    HTMLPanel lastYearsChartPanel;
    @UiField
    HTMLPanel lastMonthsChartPanel;
    @UiField
    HTMLPanel expensesMonthChartPanel;
    @UiField
    HTMLPanel expensesYearChartPanel;
    @UiField
    HTMLPanel bestClients;
    @UiField
    HTMLPanel worstClients;
    @UiField
    HTMLPanel bestServices;
    @UiField
    HTMLPanel bestProducts;
    @UiField
    HTMLPanel rankingPanel;
    @UiField
    HTMLPanel incomeOutComePanel;
    @UiField
    HTMLPanel expensesPanel;
    @UiField
    HTMLPanel incomeOutComeLabels;
    @UiField
    HTMLPanel loadingEvolutionChartsPanel;
    @UiField
    HTMLPanel loadingExpensesChartsPanel;
    @UiField
    HTMLPanel rankingChartPanel;
    @UiField
    HTMLPanel loadingRankingChartsPanel;

    private boolean endRankingClientAsyncCall;
    private boolean endRankingProductAsyncCall;
    private boolean endRankingServiceClientAsyncCall;
    private boolean endRankingWorstClientAsyncCall;

    public HomePage() {
        initWidget(binder.createAndBindUi(this));

        if (!AuthenticateUserProvider.getUser().isNoFinancialAccess()) {
            financialCharts.setVisible(true);
            showSalesExpensesEvolutionCharts();
            showExpensesDistributionCharts();
            showRankingCharts();
        }
    }

    private void showSalesExpensesEvolutionCharts() {

        financeServiceAsync.getSalesExpensesForLastMonths(GenericTools.formatDate(GenericTools.getDateWithoutTime()), new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                for (FinancesChartData data : result) {
                    data.addTooltip(data.getTooltipPrefix() + Messages.getLabel("home_evolution_income_label") + " : " + GenericTools.formatAmount(data.getValues().get(0) + ""));
                    data.addTooltip(data.getTooltipPrefix() + Messages.getLabel("home_evolution_outcome_label") + " : " + GenericTools.formatAmount(data.getValues().get(1) + ""));
                }
                Runnable onLoadCallback3 = new Runnable() {
                    public void run() {
                        AreaChart areaChart = new AreaChart(
                                createTable(Arrays.asList(Messages.getLabel("manual_date_day"),
                                        Messages.getLabel("home_evolution_income_label"),
                                        Messages.getLabel("home_evolution_outcome_label")),
                                        result),
                                createOptions(325, 200, 12,
                                        Messages.getLabel("home_evolution_income_outcome_last_days"),
                                        LegendPosition.NONE,
                                        "#9DFF67", "#B7B3B3"));
                        lastMonthsChartPanel.add(areaChart);
                        incomeOutComeLabels.setVisible(true);
                        hideEvolutionPanelLoading();
                    }
                };
                VisualizationUtils.loadVisualizationApi(onLoadCallback3, AreaChart.PACKAGE);
                super.onSuccess(result);
            }
        });

        //In gwt there's no Calendar class, so we must use deprecated method in Date class
        final int year = (new Date()).getYear() + 1900;
        financeServiceAsync.getSalesExpensesForLastYears(year, new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                if (result.isEmpty()) {
                    super.onSuccess(result);
                    return;
                }

                for (FinancesChartData data : result) {
                    //Add third value
                    double income = GenericTools.parseAmount(GenericTools.formatAmount(data.getValues().get(0) + ""));
                    double outcome = GenericTools.parseAmount(GenericTools.formatAmount(data.getValues().get(1) + ""));
                    data.getValues().add(income - outcome);

                    data.addTooltip(data.getTooltipPrefix() + Messages.getLabel("home_evolution_income_label") + " : " + GenericTools.formatAmount(data.getValues().get(0) + ""));
                    data.addTooltip(data.getTooltipPrefix() + Messages.getLabel("home_evolution_outcome_label") + " : " + GenericTools.formatAmount(data.getValues().get(1) + ""));
                    data.addTooltip(data.getTooltipPrefix() + Messages.getLabel("home_evolution_profit_label") + " : " + GenericTools.formatAmount(data.getValues().get(2) + ""));
                }
                final List<FinancesChartData> profitData = new ArrayList<FinancesChartData>();
                for (FinancesChartData data : result) {
                    FinancesChartData newProfitData = new FinancesChartData();
                    newProfitData.setTitle(data.getTitle());
                    newProfitData.addValue(data.getValues().get(2));
                    newProfitData.addTooltip(data.getTooltips().get(2));
                    profitData.add(newProfitData);
                }

                for (FinancesChartData data : result) {
                    data.getValues().remove(2);
                }

                Runnable onLoadCallbackIncomeOutCome = new Runnable() {
                    public void run() {
                        ColumnChart areaChart = new ColumnChart(
                                createTable(Arrays.asList(Messages.getLabel("manual_date_year"),
                                        Messages.getLabel("home_evolution_income_label"),
                                        Messages.getLabel("home_evolution_outcome_label")),
                                        result),
                                createOptions(325, 200, 12,
                                        Messages.getLabel("home_evolution_income_outcome_last_years"),
                                        LegendPosition.NONE,
                                        "#9DFF67", "#B7B3B3"));
                        lastYearsChartPanel.add(areaChart);
                        hideEvolutionPanelLoading();
                    }
                };

                Runnable onLoadCallbackProfit = new Runnable() {
                    public void run() {
                        ColumnChart areaChart = new ColumnChart(
                                createTable(Arrays.asList(Messages.getLabel("manual_date_year"),
                                        Messages.getLabel("home_evolution_profit_label")),
                                        profitData),
                                createOptions(300, 200, 12,
                                        Messages.getLabel("home_evolution_profit"),
                                        LegendPosition.NONE,
                                        "#B4E6FF"));
                        profitChartPanel.add(areaChart);
                        hideEvolutionPanelLoading();
                    }
                };

                VisualizationUtils.loadVisualizationApi(onLoadCallbackIncomeOutCome, ColumnChart.PACKAGE);
                VisualizationUtils.loadVisualizationApi(onLoadCallbackProfit, ColumnChart.PACKAGE);


                super.onSuccess(result);
            }
        });
    }

    private void hideEvolutionPanelLoading() {
        incomeOutComePanel.setVisible(true);
        loadingEvolutionChartsPanel.setVisible(false);
    }

    private void showExpensesDistributionCharts() {
        //In gwt there's no Calendar class, so we must use deprecated method in Date class
        final int year = (new Date()).getYear() + 1900;
        final int month = new Date().getMonth();

        financeServiceAsync.getExpensesByCategoryForMonth(month, new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                if (result.size() == 1) {
                    super.onSuccess(result);
                    return;
                }

                Runnable onLoadCallbackMonth = new Runnable() {
                    public void run() {
                        PieChart pieChart = new PieChart(
                                createTable(Arrays.asList(Messages.getLabel("result_table_expense_category"),
                                        Messages.getLabel("finaces_balance_expenses_title")),
                                        result),
                                createOptions(400, 220, 16,
                                        Month.values()[month].getLabel(),
                                        LegendPosition.LEFT,
                                        "#C39BFF", "#E4B4FF", "#D2B4FF", "#EDCEFF", "#E1CEFF", "#F1E7FF"));
                        expensesMonthChartPanel.add(pieChart);
                        hideExpensesLoadingPanel();
                    }
                };
                VisualizationUtils.loadVisualizationApi(onLoadCallbackMonth, PieChart.PACKAGE);
                super.onSuccess(result);
            }
        });

        financeServiceAsync.getExpensesByCategoryForYear(year, new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                if (result.size() == 1) {
                    LabelText label = new LabelText("nochartlabellayout");
                    label.setLabel("home_expense_no_chart");
                    expensesPanel.add(label);
                    hideExpensesLoadingPanel();
                    super.onSuccess(result);
                    return;
                }

                Runnable onLoadCallbackYear = new Runnable() {
                    public void run() {
                        PieChart pieChart = new PieChart(
                                createTable(Arrays.asList(Messages.getLabel("result_table_expense_category"),
                                        Messages.getLabel("finaces_balance_expenses_title")),
                                        result),
                                createOptions(400, 220, 16,
                                        year + "",
                                        LegendPosition.LEFT,
                                        "#FFE267", "#FFF17B", "#FFD78E", "#FFF7B5", "#FFE4B4", "#FFEDCE"));
                        expensesYearChartPanel.add(pieChart);
                        hideExpensesLoadingPanel();
                    }
                };
                VisualizationUtils.loadVisualizationApi(onLoadCallbackYear, PieChart.PACKAGE);
                super.onSuccess(result);
            }
        });
    }

    private void hideExpensesLoadingPanel() {
        loadingExpensesChartsPanel.setVisible(false);
        expensesPanel.setVisible(true);
    }

    private void endRankingAsyncCall(boolean clients, boolean worstClients, boolean products, boolean services) {
        loadingRankingChartsPanel.setVisible(false);

        endRankingClientAsyncCall = clients || endRankingClientAsyncCall;
        endRankingWorstClientAsyncCall = worstClients || endRankingWorstClientAsyncCall;
        endRankingProductAsyncCall = products || endRankingProductAsyncCall;
        endRankingServiceClientAsyncCall = services || endRankingServiceClientAsyncCall;

        if (endRankingClientAsyncCall && endRankingWorstClientAsyncCall
                && endRankingProductAsyncCall && endRankingServiceClientAsyncCall
                && !rankingChartPanel.isVisible()) {

            LabelText label = new LabelText("nochartlabellayout");
            label.setLabel("home_ranking_no_chart");

            rankingChartPanel.add(label);
            rankingChartPanel.setVisible(true);
        }
    }

    private void showRankingCharts() {
        financeServiceAsync.getBestClients(new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                if (result.isEmpty()) {
                    super.onSuccess(result);
                    endRankingAsyncCall(true, false, false, false);
                    return;
                }

                for (FinancesChartData data : result) {
                    String tooltip = data.getTooltips().get(0) + GenericTools.formatAmount(data.getValues().get(0) + "");
                    data.getTooltips().add(0, tooltip);
                }
                Runnable onLoadCallbackYear = new Runnable() {
                    public void run() {
                        BarChart barChart = new BarChart(
                                createTable(Arrays.asList(Messages.getLabel("home_client_label"),
                                        Messages.getLabel("home_client_income_label")),
                                        result),
                                createOptions(450, 200, 16,
                                        Messages.getLabel("home_best_client_income_title"),
                                        LegendPosition.NONE,
                                        "#9DFF67"));
                        bestClients.add(barChart);
                        rankingChartPanel.setVisible(true);
                        endRankingAsyncCall(true, false, false, false);
                    }
                };
                //PieChart.Package makes better vertical axis labels
                VisualizationUtils.loadVisualizationApi(onLoadCallbackYear, PieChart.PACKAGE);
                super.onSuccess(result);
            }
        });

        financeServiceAsync.getWorstClients(new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                if (result.isEmpty()) {
                    super.onSuccess(result);
                    endRankingAsyncCall(false, true, false, false);
                    return;
                }

                for (FinancesChartData data : result) {
                    String tooltip = data.getTooltips().get(0) + GenericTools.formatAmount(data.getValues().get(0) + "");
                    data.getTooltips().add(0, tooltip);
                }
                Runnable onLoadCallbackYear = new Runnable() {
                    public void run() {
                        BarChart barChart = new BarChart(
                                createTable(Arrays.asList(Messages.getLabel("home_bad_client_label"),
                                        Messages.getLabel("home_client_receivable_label")),
                                        result),
                                createOptions(450, 200, 16,
                                        Messages.getLabel("home_worst_client_income_title"),
                                        LegendPosition.NONE,
                                        "#F7CECE"));
                        worstClients.add(barChart);
                        rankingChartPanel.setVisible(true);
                        endRankingAsyncCall(false, true, false, false);
                    }
                };
                //PieChart.Package makes better vertical axis labels
                VisualizationUtils.loadVisualizationApi(onLoadCallbackYear, PieChart.PACKAGE);
                super.onSuccess(result);
            }
        });

        financeServiceAsync.getBestProducts(new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                if (result.isEmpty()) {
                    super.onSuccess(result);
                    endRankingAsyncCall(false, false, true, false);
                    return;
                }
                for (FinancesChartData data : result) {
                    String tooltip = data.getTooltips().get(0) + GenericTools.formatAmount(data.getValues().get(0) + "");
                    data.getTooltips().add(0, tooltip);
                }
                Runnable onLoadCallbackYear = new Runnable() {
                    public void run() {
                        BarChart pieChart = new BarChart(
                                createTable(Arrays.asList(Messages.getLabel("home_product_label"),
                                        Messages.getLabel("home_product_income_label")),
                                        result),
                                createOptions(450, 200, 16,
                                        Messages.getLabel("home_product_income_title"),
                                        LegendPosition.NONE,
                                        "#FFE267"));
                        bestProducts.add(pieChart);
                        rankingChartPanel.setVisible(true);
                        endRankingAsyncCall(false, false, true, false);
                    }
                };
                //PieChart.Package makes better vertical axis labels
                VisualizationUtils.loadVisualizationApi(onLoadCallbackYear, PieChart.PACKAGE);
                super.onSuccess(result);
            }
        });

        financeServiceAsync.getBestServices(new IabakoAsyncCallback<List<FinancesChartData>>() {
            public void onFailure(Throwable caught) {
                super.onFailure(caught);
                //TODO something else
            }

            public void onSuccess(final List<FinancesChartData> result) {
                if (result.isEmpty()) {
                    super.onSuccess(result);
                    endRankingAsyncCall(false, false, false, true);
                    return;
                }
                for (FinancesChartData data : result) {
                    String tooltip = data.getTooltips().get(0) + GenericTools.formatAmount(data.getValues().get(0) + "");
                    data.getTooltips().add(0, tooltip);
                }
                Runnable onLoadCallbackYear = new Runnable() {
                    public void run() {
                        BarChart pieChart = new BarChart(
                                createTable(Arrays.asList(Messages.getLabel("home_service_label"),
                                        Messages.getLabel("home_service_income_label")),
                                        result),
                                createOptions(450, 200, 16,
                                        Messages.getLabel("home_service_income_title"),
                                        LegendPosition.NONE,
                                        "#B4E6FF"));
                        bestServices.add(pieChart);
                        rankingChartPanel.setVisible(true);
                        endRankingAsyncCall(false, false, false, true);
                    }
                };
                //PieChart.Package makes better vertical axis labels
                VisualizationUtils.loadVisualizationApi(onLoadCallbackYear, PieChart.PACKAGE);
                super.onSuccess(result);
            }
        });
    }

    private Options createOptions(int width, int height, int titleSize, String title, LegendPosition legend, String... colors) {
        PieChart.PieOptions options = PieChart.PieOptions.create();
        options.setWidth(width);
        options.setHeight(height);
        options.setColors(colors);
        options.setTitle(title);

        TextStyle axisTextStyle = TextStyle.create();
        axisTextStyle.setColor("#555");
        axisTextStyle.setFontSize(9);
        AxisOptions axisOptions = AxisOptions.create();
        axisOptions.setTextStyle(axisTextStyle);
        options.setVAxisOptions(axisOptions);
        options.setHAxisOptions(axisOptions);

        TextStyle textStyle = TextStyle.create();
        textStyle.setColor("#555");
        textStyle.setFontSize(titleSize);
        textStyle.set("bold", false);
        TextStyle pieceSliceTextStyle = TextStyle.create();
        pieceSliceTextStyle.setColor("#555");
        options.setPieSliceTextStyle(pieceSliceTextStyle);
        options.setTitleTextStyle(textStyle);
        options.setLegend(legend);
        return options;
    }

    private native void addTooltipColumn(DataTable data) /*-{
        data.addColumn({
            type: 'string',
            role: 'tooltip'
        });
    }-*/;

    private AbstractDataTable createTable(List<String> titles, List<FinancesChartData> result) {
        DataTable dataTable = DataTable.create();
        dataTable.addColumn(AbstractDataTable.ColumnType.STRING, titles.get(0));
        for (int i = 1; i < titles.size(); i++) {
            dataTable.addColumn(AbstractDataTable.ColumnType.NUMBER, titles.get(i));
            addTooltipColumn(dataTable);
        }

        dataTable.addRows(result.size());
        int index = 0;
        for (FinancesChartData data : result) {
            dataTable.setValue(index++, 0, data.getTitle());
        }
        int indexDatas = 0;
        for (FinancesChartData data : result) {
            int indexValues = 1;
            int indexTooltip = 0;
            for (Double value : data.getValues()) {
                //value
                dataTable.setValue(indexDatas, indexValues++, value);
                //tooltip
                dataTable.setValue(indexDatas, indexValues++, data.getTooltips().isEmpty() ? null : data.getTooltips().get(indexTooltip++));
            }
            indexDatas++;
        }
        return dataTable;
    }
}
